package com.anXin.user.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.intern.InternUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.anXin.model.common.dtos.ResponseResult;
import com.anXin.model.common.enums.AppHttpCodeEnum;
import com.anXin.model.user.dtos.*;
import com.anXin.model.user.pojos.User;
import com.anXin.user.controller.KaptchaController;
import com.anXin.user.service.UserFanService;
import com.anXin.user.service.UserFollowService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;

import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.anXin.user.mapper.UserMapper;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import com.anXin.user.service.UserService;
import utils.common.AppJwtUtil;
import utils.common.MD5Utils;
import utils.common.RegexUtils;
import utils.thread.AppThreadLocalUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static utils.common.RedisConstants.LOGIN_CODE_KEY;
import static utils.common.RedisConstants.LOGIN_CODE_TTL;
import static utils.common.SystemConstants.INVALID_USER_NICK_NAME_PREFIX;
import static utils.common.SystemConstants.USER_NICK_NAME_PREFIX;


@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserFanService userFanService;

    @Resource
    private UserFollowService userFollowService;

    @Override
    public ResponseResult login(LoginDto dto) {
        HttpSession session= KaptchaController.session;
        String captcha = (String) session.getAttribute("captcha");
        if (!captcha.equals(dto.getCode())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_CODE_ERROR);
        }

        //1.正常登录（手机号+密码登录）
        if (!StringUtils.isBlank(dto.getPhone()) && !StringUtils.isBlank(dto.getPassword())) {
            //1.1查询用户
            User user = getOne(Wrappers.<User>lambdaQuery().eq(User::getPhone, dto.getPhone()));
            if (user == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");
            }

            //1.2 比对密码
            String salt = user.getSalt();
            String pswd = dto.getPassword();
            pswd = DigestUtils.md5DigestAsHex((pswd + salt).getBytes());
            if (!pswd.equals(user.getPassword())) {
                return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
            }
            //1.3 返回数据  jwt
            Map<String, Object> map = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(user.getId().longValue()));
            user.setSalt("");
            user.setPassword("");
            map.put("user", user);
            return ResponseResult.okResult(map);
        } else {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
        }
    }

    @Override
    public ResponseResult reg(RegDto dto) {
        User one = query().eq("phone", dto.getPhone()).one();
        if (one!=null){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONEREG_ERROR);
        }
        if (dto.getPassword().isEmpty()){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_CODE_ERROR);
        }
        if (!RegexUtils.isPasswordInvalid(dto.getPassword())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORDINVALID_ERROR);
        }

        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + dto.getPhone());

        if(code==null||code.length()==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODENULL_ERROR);
        }
        if (!code.equals(dto.getPhoneCode())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODEINVALID_ERROR);
        }

        User u=new User();
        u.setName(StringUtils.isBlank(dto.getName())?USER_NICK_NAME_PREFIX+RandomUtil.randomString(10):dto.getName());
        u.setImage(StringUtils.isBlank(dto.getImage())?"http://116.196.100.182:9000/anxin/2023/08/14/46d4dcba5588462bb571093a62798bc3.jpg":dto.getImage());
        String slat = RandomUtil.randomNumbers(new Random().nextInt(10)+5);
        u.setSalt(slat);
        u.setPassword(MD5Utils.encodeWithSalt(dto.getPassword(),slat));
        u.setSex(dto.getSex());
        u.setPhone(dto.getPhone());
        boolean save = save(u);
        if (save){
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

        }
        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
    }

    @Override
    public ResponseResult phoneCode(PhoneCodeDto dto) {
        String phone = dto.getPhone();
        String code = dto.getCode();
        if (RegexUtils.isPhoneInvalid(phone)){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONE_ERROR);
        }

        if (StringUtils.isBlank(code)){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_CODE_ERROR);
        }
        HttpSession session= KaptchaController.session;
        if(ObjectUtil.isEmpty(session.getAttribute("captcha")))return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_CODE_ERROR);
        String captcha = (String) session.getAttribute("captcha");
        if (!captcha.equals(code)){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_CODE_ERROR);
        }

        if (StringUtils.isNotEmpty(phone)){
            String phoneCode = RandomUtil.randomNumbers(6);
            log.info("手机验证码为{}",phoneCode);
            stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,phoneCode,LOGIN_CODE_TTL, TimeUnit.MINUTES);

            try{
                // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
                // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
                // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
                Credential cred = new Credential("AKIDxjJvJTQORVXbcdXREqFiRxJsHTUL4e5N", "EE3hdTCpPuFYmrwtgS5MUTGoaOE1kMtX");
                // 实例化一个http选项，可选的，没有特殊需求可以跳过
                HttpProfile httpProfile = new HttpProfile();
                httpProfile.setEndpoint("sms.tencentcloudapi.com");
                // 实例化一个client选项，可选的，没有特殊需求可以跳过
                ClientProfile clientProfile = new ClientProfile();
                clientProfile.setHttpProfile(httpProfile);
                // 实例化要请求产品的client对象,clientProfile是可选的
                SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);
                // 实例化一个请求对象,每个接口都会对应一个request对象
                SendSmsRequest req = new SendSmsRequest();
                String[] phoneNumberSet1 = {"+86"+phone};
                //下发手机号
                req.setPhoneNumberSet(phoneNumberSet1);
                //短信 SdkAppId
                req.setSmsSdkAppId("1400821245");
                //短信签名内容，使用 UTF-8 编码，必须填写已审核通过的签名
                req.setSignName("昆工吧");
                //模板 ID，必须填写已审核通过的模板 ID
                req.setTemplateId("1835290");
                String[] templateParamSet1 = {phoneCode,"5"};

                //模板参数，与模板一致，则设置为空。
                req.setTemplateParamSet(templateParamSet1);
                //短信码号扩展号，默认未开通
                // req.setExtendCode("");

                //用户的 session 内容，可以携带用户侧 ID 等上下文信息，server 会原样返回
                // req.setSessionContext("");
                //国内短信无需填写该项；国际/港澳台短信已申请独立 SenderId 需要填写该字段，默认使用公共 SenderId，无需填写该字段。
                // req.setSenderId("");
                // 返回的resp是一个SendSmsResponse的实例，与请求对象对应
                SendSmsResponse resp = client.SendSms(req);
                // 输出json格式的字符串回包
                System.out.println(SendSmsResponse.toJsonString(resp));
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            } catch (TencentCloudSDKException e) {
                System.out.println(e.toString());
                return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODE_ERROR);

            }

        }

        return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODE_ERROR);
    }

    @Override
    public ResponseResult loginByPhone(LoginByPhoneDto dto) {
        User user = query().eq("phone", dto.getPhone()).one();
        if (user==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONENULL_ERROR);
        }
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + dto.getPhone());
        if (StringUtils.isBlank(code))return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODENULL_ERROR);
        if (code.equals(dto.getPhoneCode())){
            //1.3 返回数据  jwt
            Map<String, Object> map = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(user.getId().longValue()));
            user.setSalt("");
            user.setPassword("");
            map.put("user", user);
            return ResponseResult.okResult(map);
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODEINVALID_ERROR);
    }

    @Override
    public ResponseResult queryByKey() {

        List<User> list = query().eq("status", 0).ne("id", AppThreadLocalUtil.getUser().getId()).list();
        if (list.isEmpty())return ResponseResult.errorResult(AppHttpCodeEnum.DATA_LIST_NULL);
        List<UserInfoDto> userInfoDtos = list.stream().map(user -> {
            UserInfoDto userInfoDto = BeanUtil.copyProperties(user, UserInfoDto.class);
            userInfoDto.setFansCount(userFanService.fansCount(user.getId()));
            userInfoDto.setFollowCount(userFollowService.followCount(user.getId()));
            userInfoDto.setIsfollow(userFollowService.isFollow(user.getId()).getCode() == 200);
            return userInfoDto;
        }).collect(Collectors.toList());
        return ResponseResult.okResult(userInfoDtos);
    }

    @Override
    public ResponseResult queryByKey(String key) {
        List<User> list = query().like("name", key).ne("id", AppThreadLocalUtil.getUser().getId()).eq("status", 0).list();
        if (list.isEmpty())return ResponseResult.errorResult(AppHttpCodeEnum.DATA_LIST_NULL);
        List<UserInfoDto> userInfoDtos = list.stream().map(user -> {
            UserInfoDto userInfoDto = BeanUtil.copyProperties(user, UserInfoDto.class);
            userInfoDto.setFansCount(userFanService.fansCount(user.getId()));
            userInfoDto.setFollowCount(userFollowService.followCount(user.getId()));
            userInfoDto.setIsfollow(userFollowService.isFollow(user.getId()).getCode() == 200);
            return userInfoDto;
        }).collect(Collectors.toList());
        return ResponseResult.okResult(userInfoDtos);
    }

    @Override
    public ResponseResult batchDelete(Long[] ids){

        boolean removeByIds = removeByIds(Arrays.stream(ids).collect(Collectors.toList()));
        if (removeByIds)return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);

    }

    @Override
    public ResponseResult setStatus(Long id) {
        User user = query().eq("id", id).one();
        if (user==null)return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        user.setStatus(!user.getStatus());
        if (user.getStatus()){
            user.setName(INVALID_USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
        }
        boolean update = updateById(user);
        if (update)return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
    }

    @Override
    public ResponseResult userInfo(Long id) {
        User user = query().eq("id", id).one();
        if (ObjectUtil.isEmpty(user))return ResponseResult.errorResult(AppHttpCodeEnum.DATA_LIST_NULL);
        UserInfoDto userInfoDto = BeanUtil.copyProperties(user, UserInfoDto.class);
        userInfoDto.setFansCount(userFanService.fansCount(id));
        userInfoDto.setFollowCount(userFollowService.followCount(id));
//        userInfoDto.setIsfollow(userFollowService.isFollow(id).getCode()==200);
        return ResponseResult.okResult(userInfoDto);
    }

    @Override
    public ResponseResult checkPhoneCode(LoginByPhoneDto dto) {
        User user = query().eq("phone", dto.getPhone()).one();
        if (user==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONENULL_ERROR);
        }
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + dto.getPhone());
        if (StringUtils.isBlank(code))return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODENULL_ERROR);
        if (code.equals(dto.getPhoneCode())){
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PHONECODEINVALID_ERROR);
    }

    @Override
    public ResponseResult updatePassword(UpdateDto dto) {
        if (dto.getPassword().trim().isEmpty())return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORDINVALID_ERROR);
        if (dto.getRePassword().trim().isEmpty())return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORDINVALID_ERROR);
        if (dto.getPassword().equals(dto.getRePassword())) {
            if (!RegexUtils.isPasswordInvalid(dto.getPassword())){
                return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORDINVALID_ERROR);
            }
            Long id = AppThreadLocalUtil.getUser().getId();
            User user = query().eq("id", id).one();
            user.setPassword(MD5Utils.encodeWithSalt(dto.getRePassword(),user.getSalt()));
            boolean updateById = updateById(user);
            if (updateById)return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_AGAIN_ERROR);
    }


}